<html><body>
<canvas id="Canvas" width="1080" height="720"/>

<script id="Vert" type="shader/vertex">#version 300 es
layout(location = 0) in vec3 position;
void main()
{
    gl_Position = vec4(position,1.0);
}
</script>
<script id="Frag" type="shader/fragment">#version 300 es
precision mediump float;
out vec4 fragColor;
uniform float iTime;
uniform vec2 iMouse;
uniform vec2 iResolution;


struct SurfaceHit
{
    float t;
    vec3  n;
    int   f;
    vec2 uv;
};
const SurfaceHit sNone = SurfaceHit(-1.0,vec3(0.0),0,vec2(0.0));
////////////////////////////////////////////////////////////////////////////////
SurfaceHit intersectRayAABB(vec3 ro,vec3 rd,vec3 pmin,vec3 pmax)
{
    // Basic ray/AABB intersection //
    
    vec3  tmin = (pmin-ro)/rd;
    vec3  tmax = (pmax-ro)/rd;
    vec3  sc   = min(tmin,tmax);
    vec3  sf   = max(tmin,tmax);
    float t0   = max(max(sc.x,sc.y),sc.z);
    float t1   = min(min(sf.x,sf.y),sf.z);
    if(!(t0 <= t1 && t1 > 0.0)) return sNone;
    
    // Computing additional intersection data //
    
    // Normals
    vec3 n = -sign(rd)*step(sc.yzx,sc.xyz)*step(sc.zxy,sc.xyz);
    
    // Face Id
    int f = int(dot(abs(n),vec3(1,2,4))) ^ int(any(greaterThan(n,vec3(0))));
    
    // Texture coordinates
    vec3 uv3 = ro+t0*rd;
    vec2 uv2 = 0.5+mix(uv3.xy,uv3.zz,abs(n.xy));
         uv2 = mix(uv2,vec2(1.0-uv2.x,uv2.y),max(n.x,max(n.y,-n.z)));
    
    return SurfaceHit(t0,n,f,uv2);
}
////////////////////////////////////////////////////////////////////////////////
SurfaceHit intersectRayOBB(vec3 ro,vec3 rd,mat4 m)
{
    // invm can be precomputed
    mat4 invm = inverse(m);
    
    // normal matrix used to transform AABB normals into OBB coordinates
    mat4 nm = transpose(invm);
    
    // ray transformation. rd does not need to be normalized
    vec3 roPrime = (invm*vec4(ro,1)).xyz;
    vec3 rdPrime = (invm*vec4(rd,0)).xyz;
    
    SurfaceHit s = intersectRayAABB(roPrime,rdPrime,vec3(-0.5),vec3(0.5));
    if(s.t < 0.0) return s;
    
    s.n = normalize((nm*vec4(s.n,0)).xyz);
    return s;
}
////////////////////////////////////////////////////////////////////////////////
SurfaceHit opU(const SurfaceHit s1,const SurfaceHit s2)
{
    if(s2.t < s1.t && s2.t > 0.0 || s1.t < 0.0) return s2;
    return s1;
}
////////////////////////////////////////////////////////////////////////////////
SurfaceHit castRay(vec3 ro,vec3 rd)
{
    // OBB encoded in M
    // M transforms centered, unit AABB into OBB
    // M can support all types of linear transformation; scale, shear,
    // translation, rotation, even the degenerative ones
    mat4 m1,m2,m3,m4;
    
    // Identity
    mat4 I = mat4( 1.0, 0.0, 0.0, 0.0,
                   0.0, 1.0, 0.0, 0.0,
                   0.0, 0.0, 1.0, 0.0,
                   0.0, 0.0, 0.0, 1.0);

    // Scale
    mat4 S = mat4( 1.0, 0.0, 0.0, 0.0,
                   0.0, 2.0, 0.0, 0.0,
                   0.0, 0.0, 1.0, 0.0,
                   0.0, 0.0, 0.0, 1.0);

    // Shear
    mat4 H = transpose(
             mat4( 1.0,-0.5, 0.0, 0.0,
                   0.0, 1.0, 0.0, 0.0,
                   0.0, 0.0, 1.0, 0.0,
                   0.0, 0.0, 0.0, 1.0));
                       
    // Rotation x
    float a = 3.141/4.0;
    float _c = cos(a);
    float _s = sin(a);
    mat4 Rx = transpose(
              mat4( 1.0, 0.0, 0.0, 0.0,
                    0.0,  _c, -_s, 0.0,
                    0.0,  _s,  _c, 0.0,
                    0.0, 0.0, 0.0, 1.0));
    
    // Rotation y
    mat4 Ry = transpose(
              mat4(  _c, 0.0, -_s, 0.0,
                    0.0, 1.0, 0.0, 0.0,
                     _s, 0.0,  _c, 0.0,
                    0.0, 0.0, 0.0, 1.0));
    
    // Translation
    mat4 Tx1 = transpose(
               mat4( 1.0, 0.0, 0.0, 2.0,
                     0.0, 1.0, 0.0, 0.0,
                     0.0, 0.0, 1.0, 0.0,
                     0.0, 0.0, 0.0, 1.0));
                    
    // Translation
    mat4 Tx2 = transpose(
               mat4( 1.0, 0.0, 0.0,-2.0,
                     0.0, 1.0, 0.0, 0.0,
                     0.0, 0.0, 1.0, 0.0,
                     0.0, 0.0, 0.0, 1.0));
                     
    // Translation
    mat4 Tz1 = transpose(
               mat4( 1.0, 0.0, 0.0, 0.0,
                     0.0, 1.0, 0.0, 0.0,
                     0.0, 0.0, 1.0, 2.0,
                     0.0, 0.0, 0.0, 1.0));
    m1 = I;
    m2 = Tx1*H;
    m3 = Tx2*Rx*Ry;
    m4 = Tz1*S;
    
    SurfaceHit s = sNone;
    s = opU(s,intersectRayOBB(ro,rd,m1));
    s = opU(s,intersectRayOBB(ro,rd,m2));
    s = opU(s,intersectRayOBB(ro,rd,m3));
    s = opU(s,intersectRayOBB(ro,rd,m4));
    return s;
}
////////////////////////////////////////////////////////////////////////////////
vec3 renderObjects(vec3 ro,vec3 rd)
{
	float near   = 0.5;
	float far    = 64.0;
	SurfaceHit s = castRay(ro,rd);

	vec3 color = vec3(0.0);
	if(s.t >= near && s.t <= far)
        color = abs(s.n)*0.5+vec3(s.uv,1)*0.5;

	return color;
}
////////////////////////////////////////////////////////////////////////////////
mat3 setCamera(vec3 eye,vec3 target,vec3 up)
{
	vec3 z = normalize(eye-target);
	vec3 x = normalize(cross(up,z));
	vec3 y = cross(z,x);
	return mat3(x,y,z);
}
////////////////////////////////////////////////////////////////////////////////
mat3 setProjection(float aspect,float fov)
{
    float tg = tan(fov*0.5);
    return mat3(aspect*tg, 0.0,  0.0,
                0.0,        tg,  0.0,
                0.0,       0.0, -1.0);
}
////////////////////////////////////////////////////////////////////////////////
vec3 cartesian(vec3 spherical)
{
    return vec3(spherical.x*sin(spherical.y)*sin(spherical.z),
                spherical.x*cos(spherical.y),
                spherical.x*sin(spherical.y)*cos(spherical.z));
}
////////////////////////////////////////////////////////////////////////////////
void main()
{
	vec2 fragCoord = gl_FragCoord.xy;

    vec2  mouse = iMouse.xy/iResolution.xy;
    
    vec2 screenCoords = fragCoord/iResolution.xy;
	     screenCoords = (screenCoords-vec2(0.5))*5.0;
	
    vec2 m  = vec2(mouse.x>0.0?mouse.x:cos(iTime*0.1)*0.5,mouse.y>0.0?mouse.y:0.35);
    vec3 ro = cartesian(vec3(2.0,m.y*3.14,-m.x*6.28));
	
    float fov  = 3.14/4.0;
    mat3  view = setCamera(ro,vec3(0,0.5,0.0),vec3(0,1.0,0));
    mat3  proj = setProjection(iResolution.x/iResolution.y,fov);
    mat3  vp   = view*proj;
	vec3  rd   = normalize(vp*vec3(screenCoords,1.0));

    vec3 l = normalize(vec3(0.0,1.0,-3.0));
	vec3 color  = vec3(0.0);
         color += renderObjects(ro,rd);

    fragColor = vec4(color,1.0);
}
</script>


<script type="text/javascript">
function getShader(gl,type,id)
{
	var source = document.getElementById(id).textContent;
	var shader = gl.createShader(type);

    gl.shaderSource(shader,source);
    gl.compileShader(shader);

    if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS))
        alert(gl.getShaderInfoLog(shader));

    return shader;
}
window.onload = function()
{
    var canvas = document.getElementById("Canvas");
    canvas.onmousedown = function(e) { canvas.mouseDown = true;  }
    canvas.onmouseup   = function(e) { canvas.mouseDown = false; }
    canvas.mouseX = 0;
    canvas.mouseY = 0;
    canvas.onmousemove = function(e)
    {
    	if(!canvas.mouseDown) return;

		var rect = canvas.getBoundingClientRect();
      	x = Math.floor((e.clientX-rect.left)/(rect.right-rect.left)*canvas.width);
        y = Math.floor(canvas.height-(e.clientY-rect.top)/(rect.bottom-rect.top)*canvas.height);

		canvas.mouseX = x;
		canvas.mouseY = y;
    }

    var gl = canvas.getContext("webgl2");

	var vert = getShader(gl,gl.VERTEX_SHADER,"Vert");
	var frag = getShader(gl,gl.FRAGMENT_SHADER,"Frag");

    var shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram,getShader(gl,gl.VERTEX_SHADER,"Vert"));
    gl.attachShader(shaderProgram,getShader(gl,gl.FRAGMENT_SHADER,"Frag"));
    gl.linkProgram(shaderProgram);
    gl.useProgram(shaderProgram);
    gl.enableVertexAttribArray(0);

    vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER,vbo);
    var vertices = [
    	-1.0, 1.0,1.0, -1.0,-1.0,1.0,  1.0, 1.0,1.0,
 		 1.0, 1.0,1.0, -1.0,-1.0,1.0,  1.0,-1.0,1.0, 
    ];
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
    gl.vertexAttribPointer(0,3,gl.FLOAT,false,0,0);

    gl.viewport(0,0,canvas.width,canvas.height);

	var iTimeLocation       = gl.getUniformLocation(shaderProgram,"iTime");
	var iMouseLocation      = gl.getUniformLocation(shaderProgram,"iMouse");
	var iResolutionLocation = gl.getUniformLocation(shaderProgram,"iResolution");

	var time = 0;
	window.setInterval(function()
	{
		gl.uniform1f(iTimeLocation,time);
		gl.uniform2f(iMouseLocation,canvas.mouseX,canvas.mouseY);
		gl.uniform2f(iResolutionLocation,canvas.width,canvas.height);
		time += 0.06;

    	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    	gl.drawArrays(gl.TRIANGLES,0,6);
	},0.12); // 30 FPS
};
</script>

</body></html>
